
`include "defines.v"

module SimTop (
    input               clock,
    input               reset,

    input  [`BUS_WIDTH] io_logCtrl_log_begin,
    input  [`BUS_WIDTH] io_logCtrl_log_end,
    input  [`BUS_WIDTH] io_logCtrl_log_level,
    input               io_perfInfo_clean,
    input               io_perfInfo_dump,

    output              io_uart_out_valid,
    output [7 :      0] io_uart_out_ch,
    output              io_uart_in_valid,
    input  [7 :      0] io_uart_in_ch    
);
    

    // Pc
    // Pc -> If, Mux_alu_ia, Mux_offset
    wire [`BUS_WIDTH] inst_addr;

    // Inst_rom
    // Inst_rom -> Id, Ie
    wire [31 :     0] inst;
    
    // Ctr_cpu
    // Ctr_cpu -> Ie
    wire [2 :      0] ext_op;
    // Ctr_cpu -> Reg
    wire              reg_wen;
    // Ctr_cpu -> Alu
    wire [3 :      0] alu_ctr;
    // Ctr_cpu -> Ctr_mem, Data_mem
    wire              mem_ren;
    wire              mem_wen;
    // Ctr_cpu -> Mux_alu_ia
    wire              alu_asrc;
    // Ctr_cpu -> Mux_alu_ib
    wire [1 :      0] alu_bsrc;
    // Ctr_cpu -> Mux_w_data
    wire              mem2reg;
    // Ctr_cpu -> Mux_offset
    wire [1 :      0] pc_sel;
    // Ctr_cpu -> Ctr_mem, Mux_width
    wire [2 :      0] width_sel;

    // Ctr_mem
    // Ctr_mem -> Data_mem
    wire [`BUS_WIDTH] mem_wdata;
    wire [`BUS_WIDTH] mem_wmask;
    // Ctr_mem -> Mux_w_data
    wire [`BUS_WIDTH] data_out;
    
    // Id
    // Id -> Reg_file
    wire [4 :      0] rs1;
    wire [4 :      0] rs2;
    wire [4 :      0] rd;
    // Id -> Ctr_cpu
    wire [4 :      0] op;
    wire [2 :      0] f3;
    wire              f7;

    // Reg_file
    // Reg_file -> Mux_alu_ia, Mux_offset
    wire [`BUS_WIDTH] r_data1;
    // Reg_file -> Ctr_mem, Mux_alu_ib
    wire [`BUS_WIDTH] r_data2;
    // Reg_file -> Difftest
    wire [`BUS_WIDTH] regs_o[31 : 0];
    
    // Ie
    // Ie -> Mux_alu_ib, Mux_offset
    wire [`BUS_WIDTH] imm;

    // Mux_alu_ia
    // Mux_alu_ia -> Alu
    wire [`BUS_WIDTH] alu_ia;

    // Mux_alu_ib
    // Mux_alu_ib -> Alu
    wire [`BUS_WIDTH] alu_ib;

    // Alu
    // Alu -> Ctr_cpu, Ctr_mem, Mux_w_data, Data_mem
    wire [`BUS_WIDTH] result;
    // Alu -> Ctr_cpu
    wire              zero;

    // Data_mem
    // Data_mem -> Mux_w_data
    wire [`BUS_WIDTH] mem_rdata;

    // Mux_w_data
    // Mux_w_data -> Ctr_mem
    wire [`BUS_WIDTH] mux_width_i;

    // Mux_width
    // Mux_width -> Reg_file
    wire [`BUS_WIDTH] w_data;

    // Mux_offset
    // Mux_offset -> Pc
    wire [`BUS_WIDTH] offset;


    pc Pc (
        .clk            (clock),
        .rst            (reset),

        .offset         (offset),
        .inst_addr      (inst_addr)
    );

    inst_rom Inst_rom (
        .clk            (clock),
        .rst            (reset),
        
        .inst_addr      (inst_addr),
        .inst           (inst)
    );
    
    ctr_cpu Ctr_cpu (
        .rst            (reset),
        
        .op             (op),
        .f3             (f3),
        .f7             (f7),

        .zero           (zero),
        .result         (result),

        .ext_op         (ext_op),
        .reg_wen        (reg_wen),
        .alu_ctr        (alu_ctr),
        .mem_ren        (mem_ren),
        .mem_wen        (mem_wen),
        .alu_asrc       (alu_asrc),
        .alu_bsrc       (alu_bsrc),
        .mem2reg        (mem2reg),
        .pc_sel         (pc_sel),
        .width_sel      (width_sel)
    );

    ctr_mem Ctr_mem (
        .rst            (reset),

        .mem_ren        (mem_ren),
        .mem_wen        (mem_wen),
        .addr           (result),
        .data_in        (r_data2),
        .width_sel      (width_sel),
        .data_out       (data_out),

        .mem_rdata      (mem_rdata),
        .mem_wdata      (mem_wdata),
        .mem_wmask      (mem_wmask)
    );
    
    id Id (
        .rst            (reset),
        
        .inst           (inst),

        .op             (op),
        .f3             (f3),
        .f7             (f7),
        .rs1            (rs1),
        .rs2            (rs2),
        .rd             (rd)
    );

    reg_file Reg_file (
        .clk            (clock),
        .rst            (reset), 

        .w_addr         (rd),
        .w_data         (w_data),
        .reg_wen        (reg_wen),

        .r_addr1        (rs1),
        .r_data1        (r_data1),

        .r_addr2        (rs2),
        .r_data2        (r_data2),

        .regs_o         (regs_o)
    );

    ie Ie (
        .rst            (reset),
        
        .inst           (inst),
        .ext_op         (ext_op),

        .imm            (imm)
    );

    mux_alu_ia Mux_alu_ia (
        .rst            (reset),
        
        .inst_addr      (inst_addr),
        .r_data1        (r_data1),
        .alu_asrc       (alu_asrc),

        .alu_ia         (alu_ia)
    );

    mux_alu_ib Mux_alu_ib (
        .rst            (reset),
        
        .r_data2        (r_data2),
        .imm            (imm),
        .alu_bsrc       (alu_bsrc),

        .alu_ib         (alu_ib)
    );

    alu Alu (
        .rst            (reset),
        
        .alu_ia         (alu_ia),
        .alu_ib         (alu_ib),
        .alu_ctr        (alu_ctr),

        .result         (result),
        .zero           (zero)
    );

    data_mem Data_mem (
        .clk            (clock),
        .rst            (reset), 
        
        .addr           (result),
        .mem_wdata      (mem_wdata),
        .mem_ren        (mem_ren),
        .mem_wmask      (mem_wmask),
        .mem_wen        (mem_wen),

        .mem_rdata      (mem_rdata)
    );

    mux_w_data Mux_w_data (
        .rst            (reset),
        
        .result         (result),
        .data_out       (data_out),
        .mem2reg        (mem2reg),

        .w_data         (mux_width_i)
    );

    mux_width Mux_width (
        .rst            (reset),
        
        .mux_width_i    (mux_width_i),
        .width_sel      (width_sel),

        .mux_width_o    (w_data)
    );

    mux_offset Mux_offset (
        .rst            (reset),
        
        .imm            (imm),
        .r_data1        (r_data1),
        .inst_addr      (inst_addr),
        .pc_sel         (pc_sel),

        .offset         (offset)
    );


    // Difftest
    // Difftest_instr_commit
    wire              inst_valid;
    reg               cmt_valid;
    reg  [`BUS_WIDTH] pc;
    reg  [31:      0] cmt_inst;
    reg               cmt_skip;
    reg               cmt_wen;
    reg  [7 :      0] cmt_wdest;
    reg  [`BUS_WIDTH] cmt_wdata;
    // Difftest_trap_event
    reg               trap;
    reg  [7 :      0] trap_code;
    reg  [`BUS_WIDTH] cycleCnt;
    reg  [`BUS_WIDTH] instrCnt;
    // Difftest_arch_int_reg_state
    reg  [`BUS_WIDTH] regs_diff[0 : 31];
    // Difftest_store_event
    reg               store_valid;
    reg  [`BUS_WIDTH] store_addr;
    reg  [`BUS_WIDTH] store_data;
    reg  [7 :      0] store_mask;
    // Difftest_load_event
    reg               load_valid;
    reg  [`BUS_WIDTH] load_paddr;


    assign inst_valid = (pc != `PC_START) | (inst != 32'b0);
    
    always @(negedge clock) begin
        if (reset) begin
            cmt_valid   <= 1'b0;
            pc          <= `ZERO_WORD;
            cmt_inst    <= 32'b0;
            cmt_skip    <= 1'b0;
            cmt_wen     <= 1'b0;
            cmt_wdest   <= 8'b0;
            cmt_wdata   <= `ZERO_WORD;

            trap        <= 1'b0;
            trap_code   <= 8'b0;
            cycleCnt    <= 64'b0;
            instrCnt    <= 64'b0;

            store_valid <= 1'b0;
            store_addr  <= `ZERO_WORD;
            store_data  <= `ZERO_WORD;
            store_mask  <= `ZERO_WORD;

            load_valid  <= 1'b0;
            load_paddr  <= `ZERO_WORD;
        end
        else begin
            if (~trap) begin
                cmt_valid   <= inst_valid;
                pc          <= inst_addr;
                cmt_inst    <= inst;
                cmt_skip    <= (inst == 32'ha0006a);
                cmt_wen     <= reg_wen;
                cmt_wdest   <= {3'b0, rd};
                cmt_wdata   <= w_data;
                
                trap        <= (inst == 32'h6b);
                trap_code   <= regs_o[10][7 : 0];
                cycleCnt    <= cycleCnt + 1;
                instrCnt    <= instrCnt + inst_valid;

                regs_diff   <= regs_o;

                store_valid <= (op == 5'b01000);
                store_addr  <= result;
                store_data  <= (mem_wdata & mem_wmask);                      //
                store_mask  <= {&mem_wmask[63 : 56], &mem_wmask[55 : 48], &mem_wmask[47 : 40], &mem_wmask[39 : 32], &mem_wmask[31 : 24], &mem_wmask[23 : 16], &mem_wmask[15 : 8], &mem_wmask[7 : 0]};

                load_valid  <= (op == 5'b0);
                load_paddr  <= result;
            end
        end
    end


    DifftestInstrCommit Difftest_instr_commit (
        .clock          (clock),
        .coreid         (8'b0),
        .index          (8'b0),
        .valid          (cmt_valid),
        .pc             (pc),
        .instr          (cmt_inst),
        .skip           (1'b0),
        .isRVC          (1'b0),
        .scFailed       (1'b0),
        .wen            (cmt_wen),
        .wdest          (cmt_wdest),
        .wdata          (cmt_wdata)
    );

    DifftestTrapEvent Difftest_trap_event (
        .clock          (clock),
        .coreid         (8'b0),
        .valid          (trap),
        .code           (trap_code),
        .pc             (pc),
        .cycleCnt       (cycleCnt),
        .instrCnt       (instrCnt)
    );
    
    DifftestCSRState Difftest_csr_state(
        .clock          (clock),
        .coreid         (8'b0),
        .priviledgeMode (2'b0),
        .mstatus        (`ZERO_WORD),
        .sstatus        (`ZERO_WORD),
        .mepc           (`ZERO_WORD),
        .sepc           (`ZERO_WORD),
        .mtval          (`ZERO_WORD),
        .stval          (`ZERO_WORD),
        .mtvec          (`ZERO_WORD),
        .stvec          (`ZERO_WORD),
        .mcause         (`ZERO_WORD),
        .scause         (`ZERO_WORD),
        .satp           (`ZERO_WORD),
        .mip            (`ZERO_WORD),
        .mie            (`ZERO_WORD),
        .mscratch       (`ZERO_WORD),
        .sscratch       (`ZERO_WORD),
        .mideleg        (`ZERO_WORD),
        .medeleg        (`ZERO_WORD)
    );
    
    DifftestArchIntRegState Difftest_arch_int_reg_state (
        .clock          (clock),
        .coreid         (8'b0),
        .gpr_0          (regs_diff[0]),
        .gpr_1          (regs_diff[1]),
        .gpr_2          (regs_diff[2]),
        .gpr_3          (regs_diff[3]),
        .gpr_4          (regs_diff[4]),
        .gpr_5          (regs_diff[5]),
        .gpr_6          (regs_diff[6]),
        .gpr_7          (regs_diff[7]),
        .gpr_8          (regs_diff[8]),
        .gpr_9          (regs_diff[9]),
        .gpr_10         (regs_diff[10]),
        .gpr_11         (regs_diff[11]),
        .gpr_12         (regs_diff[12]),
        .gpr_13         (regs_diff[13]),
        .gpr_14         (regs_diff[14]),
        .gpr_15         (regs_diff[15]),
        .gpr_16         (regs_diff[16]),
        .gpr_17         (regs_diff[17]),
        .gpr_18         (regs_diff[18]),
        .gpr_19         (regs_diff[19]),
        .gpr_20         (regs_diff[20]),
        .gpr_21         (regs_diff[21]),
        .gpr_22         (regs_diff[22]),
        .gpr_23         (regs_diff[23]),
        .gpr_24         (regs_diff[24]),
        .gpr_25         (regs_diff[25]),
        .gpr_26         (regs_diff[26]),
        .gpr_27         (regs_diff[27]),
        .gpr_28         (regs_diff[28]),
        .gpr_29         (regs_diff[29]),
        .gpr_30         (regs_diff[30]),
        .gpr_31         (regs_diff[31])    
    );

    DifftestStoreEvent Difftest_store_event (
        .clock          (clock),
        .coreid         (8'b0),
        .index          (8'b0),
        .valid          (1'b0),                  //
        .storeAddr      (store_addr),
        .storeData      (store_data),
        .storeMask      (store_mask)
    );

    DifftestLoadEvent Difftest_load_event (
        .clock          (clock),
        .coreid         (8'b0),
        .index          (8'b0),
        .valid          (load_valid),
        .paddr          (load_paddr),
        .opType         (8'b0),
        .fuType         (8'b0)
    );


endmodule
